package com.qaplus.plugin;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

import org.mybatis.generator.api.GeneratedJavaFile;
import org.mybatis.generator.api.GeneratedXmlFile;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.ShellRunner;
import org.mybatis.generator.api.dom.java.CompilationUnit;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.Interface;
import org.mybatis.generator.api.dom.java.JavaVisibility;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.Parameter;
import org.mybatis.generator.api.dom.java.TopLevelClass;
import org.mybatis.generator.api.dom.xml.Attribute;
import org.mybatis.generator.api.dom.xml.Document;
import org.mybatis.generator.api.dom.xml.Element;
import org.mybatis.generator.api.dom.xml.TextElement;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.codegen.XmlConstants;
import org.mybatis.generator.config.PropertyRegistry;

/**
 * Mybatis-generator for mysql dataBase
 *
 */
public class MyBatisPaginationPlugin extends PluginAdapter {
	
	
	private static String  XMLFILE_POSTFIX="Ext";
	
	private static String  JAVAFILE_POTFIX="Ext";
	
	private static String  SQLMAP_COMMON_POTFIX="and is_deleted = 'N'";
	
	private static String  ANNOTATION_RESOURCE="org.springframework.stereotype.Repository";
	
	private static String  MAPPER_ANNOTATION="@Repository";
	
	private static String  BAISE_CONDITION=" where is_deleted = 'N' ";
	

	//添删改Document的sql语句及属性
	@Override
	public boolean sqlMapDocumentGenerated(Document document,
			IntrospectedTable introspectedTable) {
		XmlElement parentElement = document.getRootElement();
		//修改Mapper.xml中的namespace
		updateDocumentNameSpace(introspectedTable, parentElement);
		generateMySqlPageSql(parentElement);
		moveDocumentInsertSql(parentElement);
		return super.sqlMapDocumentGenerated(document, introspectedTable);
	}

	//去除自带的insert
	private void moveDocumentInsertSql(XmlElement parentElement) {
		XmlElement insertElement = null;
		for (Element element : parentElement.getElements()) {
			XmlElement xmlElement = (XmlElement) element;
			if (xmlElement.getName().equals("insert")) {
				for (Attribute attribute : xmlElement.getAttributes()) {
					if (attribute.getValue().equals("insert")) {
						insertElement = xmlElement;
						break;
					}
				}
			}
		}
		parentElement.getElements().remove(insertElement);
	}
	
	private void updateDocumentNameSpace(IntrospectedTable introspectedTable,
			XmlElement parentElement) {
		Attribute  namespaceAttribute=null;
		for(Attribute attribute:parentElement.getAttributes()){
			if(attribute.getName().equals("namespace")){
				namespaceAttribute=attribute;
			}
		}
		parentElement.getAttributes().remove(namespaceAttribute);
		parentElement.getAttributes().add(new Attribute("namespace", introspectedTable.getMyBatis3JavaMapperType()+JAVAFILE_POTFIX));
	}
	
	private void generateMySqlPageSql(XmlElement parentElement) {
		// mysql分页语句后半部分
		XmlElement paginationSuffixElement = new XmlElement("sql");
		context.getCommentGenerator().addComment(paginationSuffixElement);
		paginationSuffixElement.addAttribute(new Attribute("id",
				"MySqlDialectSuffix"));
		XmlElement pageEnd = new XmlElement("if");
		pageEnd.addAttribute(new Attribute("test", "page != null"));
		pageEnd.addElement(new TextElement(
				"<![CDATA[  limit #{page.begin},#{page.end}  ]]>"));
		paginationSuffixElement.addElement(pageEnd);
		parentElement.addElement(paginationSuffixElement);
	}
	
 
	// selectByPrimaryKey
	@Override
	public boolean sqlMapSelectByPrimaryKeyElementGenerated(XmlElement element,
			IntrospectedTable introspectedTable) {
		TextElement text = new TextElement(SQLMAP_COMMON_POTFIX);
		element.addElement(text);
		return super.sqlMapSelectByPrimaryKeyElementGenerated(element,
				introspectedTable);
	}

	//updateByPrimaryKeySelective
	@Override
	public boolean sqlMapUpdateByPrimaryKeySelectiveElementGenerated(
			XmlElement element, IntrospectedTable introspectedTable) {
		List<Element> elements = element.getElements();
		XmlElement setItem=null;
		int modifierItemIndex=-1;
		int gmtModifiedItemIndex=-1;
		for(Element e:elements){
			if(e instanceof XmlElement){
				setItem=(XmlElement) e;
				for(int i=0;i<setItem.getElements().size();i++){
					XmlElement xmlElement=(XmlElement) setItem.getElements().get(i);
					for(Attribute att:xmlElement.getAttributes()){
						if (att.getValue().equals("recUpdateUser != null")) {
							modifierItemIndex = i;
                             break;
						}
						
						if (att.getValue().equals("recUpdateDate != null")) {
							gmtModifiedItemIndex=i;
						     break;
						}
						
					}
				}
			}
			
		}
		
		if(modifierItemIndex!=-1&& setItem!=null){
			addXmlElementRecUpdateUser(setItem, modifierItemIndex);
			
		}
		
		if(gmtModifiedItemIndex!=-1&& setItem!=null){
			addXmlElementRecUpdateDate(setItem, gmtModifiedItemIndex);
		}
		
		TextElement text = new TextElement(SQLMAP_COMMON_POTFIX);
		element.addElement(text);
		return super.sqlMapUpdateByPrimaryKeySelectiveElementGenerated(element,
				introspectedTable);
	}


	private void addXmlElementRecUpdateDate(XmlElement setItem,
			int gmtModifiedItemIndex) {
		XmlElement defaultGmtModified= new XmlElement("if");
		defaultGmtModified.addAttribute(new Attribute("test", "recUpdateDate == null"));
		defaultGmtModified.addElement(new TextElement("rec_update_date = NOW(),"));
		setItem.getElements().add(gmtModifiedItemIndex+1, defaultGmtModified);
	}


	private void addXmlElementRecUpdateUser(XmlElement setItem, int modifierItemIndex) {
		XmlElement defaultmodifier= new XmlElement("if");
		defaultmodifier.addAttribute(new Attribute("test", "recUpdateUser == null"));
		defaultmodifier.addElement(new TextElement("rec_update_user = 'system',"));
		setItem.getElements().add(modifierItemIndex+1, defaultmodifier);
	}

	// updateByPrimaryKey
	@Override
	public boolean sqlMapUpdateByPrimaryKeyWithoutBLOBsElementGenerated(
			XmlElement element, IntrospectedTable introspectedTable) {
		TextElement text = new TextElement(SQLMAP_COMMON_POTFIX);
		element.addElement(text);
		return super.sqlMapUpdateByPrimaryKeyWithoutBLOBsElementGenerated(
				element, introspectedTable);
	}

	//insertSelective
	public boolean sqlMapInsertSelectiveElementGenerated(XmlElement element,
			IntrospectedTable introspectedTable) {
		Attribute attKeyProperty=new Attribute("keyProperty", "id");
		Attribute attUseGeneratedKeys=new Attribute("useGeneratedKeys", "true");
		element.addAttribute(attKeyProperty);
		element.addAttribute(attUseGeneratedKeys);
		List<Element> elements = element.getElements();
		XmlElement fieldItem = null;
		XmlElement valueItem = null;
		for (Element e : elements) {
			if (e instanceof XmlElement ) {
				XmlElement xmlElement= (XmlElement) e;
				if(xmlElement.getName().equals("trim")){
					for(Attribute arr:xmlElement.getAttributes()){
						if(arr.getValue().equals("(")){
							fieldItem=xmlElement;
							break;
						}
						
					    if(arr.getValue().equals("values (")){
					    	valueItem=xmlElement;
					    	break;
					    }
					}
				}
			}
		}

		if(fieldItem!=null){
			XmlElement defaultIsDeleted= new XmlElement("if");
			defaultIsDeleted.addAttribute(new Attribute("test", "isDeleted == null"));
			defaultIsDeleted.addElement(new TextElement("is_deleted,"));
			fieldItem.addElement(1,defaultIsDeleted);
			
			XmlElement defaultGmtModified= new XmlElement("if");
			defaultGmtModified.addAttribute(new Attribute("test", "recCreateDate == null"));
			defaultGmtModified.addElement(new TextElement("rec_create_date,"));
			fieldItem.addElement(1,defaultGmtModified);
			
			XmlElement defaultmodifier= new XmlElement("if");
			defaultmodifier.addAttribute(new Attribute("test", "recUpdateDate == null"));
			defaultmodifier.addElement(new TextElement("rec_update_date,"));
			fieldItem.addElement(1,defaultmodifier);
			
			XmlElement defaultGmtCreate= new XmlElement("if");
			defaultGmtCreate.addAttribute(new Attribute("test", "recUpdateUser == null"));
			defaultGmtCreate.addElement(new TextElement("rec_update_user,"));
			fieldItem.addElement(1,defaultGmtCreate);
			
		}
		
		if(valueItem!=null){
			XmlElement defaultIsDeleted= new XmlElement("if");
			defaultIsDeleted.addAttribute(new Attribute("test", "isDeleted == null"));
			defaultIsDeleted.addElement(new TextElement("'N',"));
			valueItem.addElement(1,defaultIsDeleted);
			
			XmlElement defaultGmtCreate= new XmlElement("if");
			defaultGmtCreate.addAttribute(new Attribute("test", "recCreateDate == null"));
			defaultGmtCreate.addElement(new TextElement("NOW(),"));
			valueItem.addElement(1,defaultGmtCreate);
			
			XmlElement defaultmodifier= new XmlElement("if");
			defaultmodifier.addAttribute(new Attribute("test", "recUpdateDate == null"));
			defaultmodifier.addElement(new TextElement("NOW(),"));
			valueItem.addElement(1,defaultmodifier);
			
			XmlElement defaultGmtModified= new XmlElement("if");
			defaultGmtModified.addAttribute(new Attribute("test", "recUpdateUser == null"));
			defaultGmtModified.addElement(new TextElement("'system',"));
			valueItem.addElement(1,defaultGmtModified);
			
		}
		
		
		
		return super.sqlMapInsertSelectiveElementGenerated(element, introspectedTable);
	}

	@Override
	public boolean clientDeleteByPrimaryKeyMethodGenerated(Method method,
			Interface interfaze, IntrospectedTable introspectedTable) {
		Parameter parameter = new Parameter(new FullyQualifiedJavaType(
				introspectedTable.getBaseRecordType()), "record");
		method.getParameters().clear();
		method.addParameter(parameter);
		return super.clientDeleteByPrimaryKeyMethodGenerated(method, interfaze,
				introspectedTable);
	}

	@Override
	public boolean clientGenerated(Interface interfaze,
			TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		List<Method> methods = interfaze.getMethods();
		Method insertMethod = null;
		for (Method method : methods) {
			if (method.getName().equals("insert")) {
				insertMethod = method;
				break;
			}
		}
		methods.remove(insertMethod);

		Method updateMethod = null;
		for (Method method : methods) {
			if (method.getName().equals("updateByPrimaryKey")) {
				updateMethod = method;
				break;
			}
		}
		methods.remove(updateMethod);

		return super.clientGenerated(interfaze, topLevelClass,
				introspectedTable);
	}

	// selectByExample
	@Override
	public boolean sqlMapSelectByExampleWithoutBLOBsElementGenerated(
			XmlElement element, IntrospectedTable introspectedTable) {
		
		XmlElement lastXmlE = (XmlElement) element.getElements().remove(
				element.getElements().size() - 1);
 
		XmlElement isdeletedElement = new XmlElement("if");
		isdeletedElement.addAttribute(new Attribute("test",
				" null!=oredCriteria and oredCriteria.size != 0"));
		isdeletedElement.addElement(new TextElement(SQLMAP_COMMON_POTFIX));
		element.addElement(isdeletedElement);
		
		
		isdeletedElement = new XmlElement("if");
		isdeletedElement.addAttribute(new Attribute("test",
				" null!=oredCriteria and oredCriteria.size == 0"));
		isdeletedElement.addElement(new TextElement(BAISE_CONDITION));
		element.addElement(isdeletedElement);
		
		element.addElement(lastXmlE);

		return super.sqlMapSelectByExampleWithoutBLOBsElementGenerated(element,
				introspectedTable);
	}

	// countByExample
	@Override
	public boolean sqlMapCountByExampleElementGenerated(XmlElement element,
			IntrospectedTable introspectedTable) {
		XmlElement isNotNullElement = new XmlElement("if");
		isNotNullElement.addAttribute(new Attribute("test",
				" null!=oredCriteria and oredCriteria.size != 0"));
		isNotNullElement.addElement(new TextElement(SQLMAP_COMMON_POTFIX));
		element.addElement(isNotNullElement);
		isNotNullElement = new XmlElement("if");
		isNotNullElement.addAttribute(new Attribute("test",
				" null!=oredCriteria and oredCriteria.size == 0"));
		isNotNullElement.addElement(new TextElement(BAISE_CONDITION));
		element.addElement(isNotNullElement);
		
		return super.sqlMapCountByExampleElementGenerated(element,
				introspectedTable);
	}

	// 生成XXExt.xml
	@Override
	public List<GeneratedXmlFile> contextGenerateAdditionalXmlFiles(
			IntrospectedTable introspectedTable) {

		String[] splitFile = introspectedTable.getMyBatis3XmlMapperFileName()
				.split("\\.");
		String fileNameExt = null;
		if (splitFile[0] != null) {
			fileNameExt = splitFile[0] +XMLFILE_POSTFIX+".xml";
		}

		if (isExistExtFile(context.getSqlMapGeneratorConfiguration()
				.getTargetProject(),
				introspectedTable.getMyBatis3XmlMapperPackage(), fileNameExt)) {
			return super.contextGenerateAdditionalXmlFiles(introspectedTable);
		}

		Document document = new Document(
				XmlConstants.MYBATIS3_MAPPER_PUBLIC_ID,
				XmlConstants.MYBATIS3_MAPPER_SYSTEM_ID);

		XmlElement root = new XmlElement("mapper");
		document.setRootElement(root);
		String namespace = introspectedTable.getMyBatis3SqlMapNamespace()+XMLFILE_POSTFIX;
		root.addAttribute(new Attribute("namespace", namespace));

		GeneratedXmlFile gxf = new GeneratedXmlFile(document, fileNameExt,
				introspectedTable.getMyBatis3XmlMapperPackage(), context
						.getSqlMapGeneratorConfiguration().getTargetProject(),
				false, context.getXmlFormatter());

		List<GeneratedXmlFile> answer = new ArrayList<GeneratedXmlFile>(1);
		answer.add(gxf);

		return answer;
	}

	// 生成XXExt.java
	@Override
	public List<GeneratedJavaFile> contextGenerateAdditionalJavaFiles(
			IntrospectedTable introspectedTable) {

		FullyQualifiedJavaType type = new FullyQualifiedJavaType(
				introspectedTable.getMyBatis3JavaMapperType() + JAVAFILE_POTFIX);
		Interface interfaze = new Interface(type);
		interfaze.setVisibility(JavaVisibility.PUBLIC);
		context.getCommentGenerator().addJavaFileComment(interfaze);

		FullyQualifiedJavaType baseInterfaze = new FullyQualifiedJavaType(
				introspectedTable.getMyBatis3JavaMapperType());
		interfaze.addSuperInterface(baseInterfaze);
		
		FullyQualifiedJavaType annotation = new FullyQualifiedJavaType(ANNOTATION_RESOURCE);
		interfaze.addAnnotation(MAPPER_ANNOTATION);
		interfaze.addImportedType(annotation);
	

		CompilationUnit compilationUnits = interfaze;
		GeneratedJavaFile generatedJavaFile = new GeneratedJavaFile(
				compilationUnits,
				context.getJavaModelGeneratorConfiguration().getTargetProject(),
				context.getProperty(PropertyRegistry.CONTEXT_JAVA_FILE_ENCODING),
				context.getJavaFormatter());
		
		if (isExistExtFile(generatedJavaFile.getTargetProject(),
				generatedJavaFile.getTargetPackage(),
				generatedJavaFile.getFileName())) {
			return super.contextGenerateAdditionalJavaFiles(introspectedTable);
		}
		List<GeneratedJavaFile> generatedJavaFiles = new ArrayList<GeneratedJavaFile>(
				1);
		generatedJavaFile.getFileName();
		generatedJavaFiles.add(generatedJavaFile);
		return generatedJavaFiles;
	}

	private boolean isExistExtFile(String targetProject, String targetPackage,
			String fileName) {

		File project = new File(targetProject);
		if (!project.isDirectory()) {
			return true;
		}

		StringBuilder sb = new StringBuilder();
		StringTokenizer st = new StringTokenizer(targetPackage, ".");
		while (st.hasMoreTokens()) {
			sb.append(st.nextToken());
			sb.append(File.separatorChar);
		}

		File directory = new File(project, sb.toString());
		if (!directory.isDirectory()) {
			boolean rc = directory.mkdirs();
			if (!rc) {
				return true;
			}
		}

		File testFile = new File(directory, fileName);
		if (testFile.exists()) {
			return true;
		} else {
			return false;
		}
	}

	public boolean validate(List<String> warnings) {
		return true;
	}

	public static void main(String[] args) {
		String config = MyBatisPaginationPlugin.class.getClassLoader()
				.getResource("generatorConfig.xml").getFile();
		String[] arg = { "-configfile", config, "-overwrite" };
		ShellRunner.main(arg);
	}

}
